/**
 * jquery.slitslider.js v1.1.0
 * http://www.codrops.com
 *
 * Licensed under the MIT license.
 * http://www.opensource.org/licenses/mit-license.php
 *
 * Copyright 2012, Codrops
 * http://www.codrops.com
 */

;
(function ($, window, undefined) {

    'use strict';

    /*
     * debouncedresize: special jQuery event that happens once after a window resize
     *
     * latest version and complete README available on Github:
     * https://github.com/louisremi/jquery-smartresize/blob/master/jquery.debouncedresize.js
     *
     * Copyright 2011 @louis_remi
     * Licensed under the MIT license.
     */
    var $event = $.event,
        $special,
        resizeTimeout;

    $special = $event.special.debouncedresize = {
        setup: function () {
            $(this).on("resize", $special.handler);
        },
        teardown: function () {
            $(this).off("resize", $special.handler);
        },
        handler: function (event, execAsap) {
            // Save the context
            var context = this,
                args = arguments,
                dispatch = function () {
                    // set correct event type
                    event.type = "debouncedresize";
                    $event.dispatch.apply(context, args);
                };

            if (resizeTimeout) {
                clearTimeout(resizeTimeout);
            }

            execAsap ?
                dispatch() :
                resizeTimeout = setTimeout(dispatch, $special.threshold);
        },
        threshold: 20
    };

    // global
    var $window = $(window),
        $document = $(document),
        Modernizr = window.Modernizr;

    $.Slitslider = function (options, element) {

        this.$elWrapper = $(element);
        this._init(options);

    };

    $.Slitslider.defaults = {
        // transitions speed
        speed: 800,
        // if true the item's slices will also animate the opacity value
        optOpacity: false,
        // amount (%) to translate both slices - adjust as necessary
        translateFactor: 230,
        // maximum possible angle
        maxAngle: 25,
        // maximum possible scale
        maxScale: 2,
        // slideshow on / off
        autoplay: false,
        // keyboard navigation
        keyboard: true,
        // time between transitions
        interval: 4000,
        // callbacks
        onBeforeChange: function (slide, idx) {
            return false;
        },
        onAfterChange: function (slide, idx) {
            return false;
        }
    };

    $.Slitslider.prototype = {

        _init: function (options) {

            // options
            this.options = $.extend(true, {}, $.Slitslider.defaults, options);

            // https://github.com/twitter/bootstrap/issues/2870
            this.transEndEventNames = {
                'WebkitTransition': 'webkitTransitionEnd',
                'MozTransition': 'transitionend',
                'OTransition': 'oTransitionEnd',
                'msTransition': 'MSTransitionEnd',
                'transition': 'transitionend'
            };
            this.transEndEventName = this.transEndEventNames[Modernizr.prefixed('transition')];
            // suport for css 3d transforms and css transitions
            this.support = Modernizr.csstransitions && Modernizr.csstransforms3d;
            // the slider
            this.$el = this.$elWrapper.children('.sl-slider');
            // the slides
            this.$slides = this.$el.children('.sl-slide').hide();
            // total slides
            this.slidesCount = this.$slides.length;
            // current slide
            this.current = 0;
            // control if it's animating
            this.isAnimating = false;
            // get container size
            this._getSize();
            // layout
            this._layout();
            // load some events
            this._loadEvents();
            // slideshow
            if (this.options.autoplay) {

                this._startSlideshow();

            }

        },
        // gets the current container width & height
        _getSize: function () {

            this.size = {
                width: this.$elWrapper.outerWidth(true),
                height: this.$elWrapper.outerHeight(true)
            };

        },
        _layout: function () {

            this.$slideWrapper = $('<div class="sl-slides-wrapper" />');

            // wrap the slides
            this.$slides.wrapAll(this.$slideWrapper).each(function (i) {

                var $slide = $(this),
                // vertical || horizontal
                    orientation = $slide.data('orientation');

                $slide.addClass('sl-slide-' + orientation)
                    .children()
                    .wrapAll('<div class="sl-content-wrapper" />')
                    .wrapAll('<div class="sl-content" />');

            });

            // set the right size of the slider/slides for the current window size
            this._setSize();
            // show first slide
            this.$slides.eq(this.current).show();

        },
        _navigate: function (dir, pos) {

            if (this.isAnimating || this.slidesCount < 2) {

                return false;

            }

            this.isAnimating = true;

            var self = this,
                $currentSlide = this.$slides.eq(this.current);

            // if position is passed
            if (pos !== undefined) {

                this.current = pos;

            }
            // if not check the boundaries
            else if (dir === 'next') {

                this.current = this.current < this.slidesCount - 1 ? ++this.current : 0;

            }
            else if (dir === 'prev') {

                this.current = this.current > 0 ? --this.current : this.slidesCount - 1;

            }

            this.options.onBeforeChange($currentSlide, this.current);

            // next slide to be shown
            var $nextSlide = this.$slides.eq(this.current),
            // the slide we want to cut and animate
                $movingSlide = ( dir === 'next' ) ? $currentSlide : $nextSlide,

            // the following are the data attrs set for each slide
                configData = $movingSlide.data(),
                config = {};

            config.orientation = configData.orientation || 'horizontal',
                config.slice1angle = configData.slice1Rotation || 0,
                config.slice1scale = configData.slice1Scale || 1,
                config.slice2angle = configData.slice2Rotation || 0,
                config.slice2scale = configData.slice2Scale || 1;

            this._validateValues(config);

            var cssStyle = config.orientation === 'horizontal' ? {
                    marginTop: -this.size.height / 2
                } : {
                    marginLeft: -this.size.width / 2
                },
            // default slide's slices style
                resetStyle = {
                    'transform': 'translate(0%,0%) rotate(0deg) scale(1)',
                    opacity: 1
                },
            // slice1 style
                slice1Style = config.orientation === 'horizontal' ? {
                    'transform': 'translateY(-' + this.options.translateFactor + '%) rotate(' + config.slice1angle + 'deg) scale(' + config.slice1scale + ')'
                } : {
                    'transform': 'translateX(-' + this.options.translateFactor + '%) rotate(' + config.slice1angle + 'deg) scale(' + config.slice1scale + ')'
                },
            // slice2 style
                slice2Style = config.orientation === 'horizontal' ? {
                    'transform': 'translateY(' + this.options.translateFactor + '%) rotate(' + config.slice2angle + 'deg) scale(' + config.slice2scale + ')'
                } : {
                    'transform': 'translateX(' + this.options.translateFactor + '%) rotate(' + config.slice2angle + 'deg) scale(' + config.slice2scale + ')'
                };

            if (this.options.optOpacity) {

                slice1Style.opacity = 0;
                slice2Style.opacity = 0;

            }

            // we are adding the classes sl-trans-elems and sl-trans-back-elems to the slide that is either coming "next"
            // or going "prev" according to the direction.
            // the idea is to make it more interesting by giving some animations to the respective slide's elements
            //( dir === 'next' ) ? $nextSlide.addClass( 'sl-trans-elems' ) : $currentSlide.addClass( 'sl-trans-back-elems' );

            $currentSlide.removeClass('sl-trans-elems');

            var transitionProp = {
                'transition': 'all ' + this.options.speed + 'ms ease-in-out'
            };

            // add the 2 slices and animate them
            $movingSlide.css('z-index', this.slidesCount)
                .find('div.sl-content-wrapper')
                .wrap($('<div class="sl-content-slice" />').css(transitionProp))
                .parent()
                .cond(
                dir === 'prev',
                function () {

                    var slice = this;
                    this.css(slice1Style);
                    setTimeout(function () {

                        slice.css(resetStyle);

                    }, 50);

                },
                function () {

                    var slice = this;
                    setTimeout(function () {

                        slice.css(slice1Style);

                    }, 50);

                }
            )
                .clone()
                .appendTo($movingSlide)
                .cond(
                dir === 'prev',
                function () {

                    var slice = this;
                    this.css(slice2Style);
                    setTimeout(function () {

                        $currentSlide.addClass('sl-trans-back-elems');

                        if (self.support) {

                            slice.css(resetStyle).on(self.transEndEventName, function () {

                                self._onEndNavigate(slice, $currentSlide, dir);

                            });

                        }
                        else {

                            self._onEndNavigate(slice, $currentSlide, dir);

                        }

                    }, 50);

                },
                function () {

                    var slice = this;
                    setTimeout(function () {

                        $nextSlide.addClass('sl-trans-elems');

                        if (self.support) {

                            slice.css(slice2Style).on(self.transEndEventName, function () {

                                self._onEndNavigate(slice, $currentSlide, dir);

                            });

                        }
                        else {

                            self._onEndNavigate(slice, $currentSlide, dir);

                        }

                    }, 50);

                }
            )
                .find('div.sl-content-wrapper')
                .css(cssStyle);

            $nextSlide.show();

        },
        _validateValues: function (config) {

            // OK, so we are restricting the angles and scale values here.
            // This is to avoid the slices wrong sides to be shown.
            // you can adjust these values as you wish but make sure you also ajust the
            // paddings of the slides and also the options.translateFactor value and scale data attrs
            if (config.slice1angle > this.options.maxAngle || config.slice1angle < -this.options.maxAngle) {

                config.slice1angle = this.options.maxAngle;

            }
            if (config.slice2angle > this.options.maxAngle || config.slice2angle < -this.options.maxAngle) {

                config.slice2angle = this.options.maxAngle;

            }
            if (config.slice1scale > this.options.maxScale || config.slice1scale <= 0) {

                config.slice1scale = this.options.maxScale;

            }
            if (config.slice2scale > this.options.maxScale || config.slice2scale <= 0) {

                config.slice2scale = this.options.maxScale;

            }
            if (config.orientation !== 'vertical' && config.orientation !== 'horizontal') {

                config.orientation = 'horizontal'

            }

        },
        _onEndNavigate: function ($slice, $oldSlide, dir) {

            // reset previous slide's style after next slide is shown
            var $slide = $slice.parent(),
                removeClasses = 'sl-trans-elems sl-trans-back-elems';

            // remove second slide's slice
            $slice.remove();
            // unwrap..
            $slide.css('z-index', 1)
                .find('div.sl-content-wrapper')
                .unwrap();

            // hide previous current slide
            $oldSlide.hide().removeClass(removeClasses);
            $slide.removeClass(removeClasses);
            // now we can navigate again..
            this.isAnimating = false;
            this.options.onAfterChange($slide, this.current);

        },
        _setSize: function () {

            // the slider and content wrappers will have the window's width and height
            var cssStyle = {
                width: this.size.width,
                height: this.size.height
            };

            this.$el.css(cssStyle).find('div.sl-content-wrapper').css(cssStyle);

        },
        _loadEvents: function () {

            var self = this;

            $window.on('debouncedresize.slitslider', function (event) {

                // update size values
                self._getSize();
                // set the sizes again
                self._setSize();

            });

            if (this.options.keyboard) {

                $document.on('keydown.slitslider', function (e) {

                    var keyCode = e.keyCode || e.which,
                        arrow = {
                            left: 37,
                            up: 38,
                            right: 39,
                            down: 40
                        };

                    switch (keyCode) {

                        case arrow.left :

                            self._stopSlideshow();
                            self._navigate('prev');
                            break;

                        case arrow.right :

                            self._stopSlideshow();
                            self._navigate('next');
                            break;

                    }

                });

            }

        },
        _startSlideshow: function () {

            var self = this;

            this.slideshow = setTimeout(function () {

                self._navigate('next');

                if (self.options.autoplay) {

                    self._startSlideshow();

                }

            }, this.options.interval);

        },
        _stopSlideshow: function () {

            if (this.options.autoplay) {

                clearTimeout(this.slideshow);
                this.isPlaying = false;
                this.options.autoplay = false;

            }

        },
        _destroy: function (callback) {

            this.$el.off('.slitslider').removeData('slitslider');
            $window.off('.slitslider');
            $document.off('.slitslider');
            this.$slides.each(function (i) {

                var $slide = $(this),
                    $content = $slide.find('div.sl-content').children();

                $content.appendTo($slide);
                $slide.children('div.sl-content-wrapper').remove();

            });
            this.$slides.unwrap(this.$slideWrapper).hide();
            this.$slides.eq(0).show();
            if (callback) {

                callback.call();

            }

        },
        // public methos: adds more slides to the slider
        add: function ($slides, callback) {

            this.$slides = this.$slides.add($slides);

            var self = this;


            $slides.each(function (i) {

                var $slide = $(this),
                // vertical || horizontal
                    orientation = $slide.data('orientation');

                $slide.hide().addClass('sl-slide-' + orientation)
                    .children()
                    .wrapAll('<div class="sl-content-wrapper" />')
                    .wrapAll('<div class="sl-content" />')
                    .end()
                    .appendTo(self.$el.find('div.sl-slides-wrapper'));

            });

            this._setSize();

            this.slidesCount = this.$slides.length;

            if (callback) {

                callback.call($items);

            }

        },
        // public method: shows next slide
        next: function () {

            this._stopSlideshow();
            this._navigate('next');

        },
        // public method: shows previous slide
        previous: function () {

            this._stopSlideshow();
            this._navigate('prev');

        },
        // public method: goes to a specific slide
        jump: function (pos) {

            pos -= 1;

            if (pos === this.current || pos >= this.slidesCount || pos < 0) {

                return false;

            }

            this._stopSlideshow();
            this._navigate(pos > this.current ? 'next' : 'prev', pos);

        },
        // public method: starts the slideshow
        // any call to next(), previous() or jump() will stop the slideshow
        play: function () {

            if (!this.isPlaying) {

                this.isPlaying = true;

                this._navigate('next');
                this.options.autoplay = true;
                this._startSlideshow();

            }

        },
        // public method: pauses the slideshow
        pause: function () {

            if (this.isPlaying) {

                this._stopSlideshow();

            }

        },
        // public method: check if isAnimating is true
        isActive: function () {

            return this.isAnimating;

        },
        // publicc methos: destroys the slicebox instance
        destroy: function (callback) {

            this._destroy(callback);

        }

    };

    var logError = function (message) {

        if (window.console) {

            window.console.error(message);

        }

    };

    $.fn.slitslider = function (options) {

        var self = $.data(this, 'slitslider');

        if (typeof options === 'string') {

            var args = Array.prototype.slice.call(arguments, 1);

            this.each(function () {

                if (!self) {

                    logError("cannot call methods on slitslider prior to initialization; " +
                    "attempted to call method '" + options + "'");
                    return;

                }

                if (!$.isFunction(self[options]) || options.charAt(0) === "_") {

                    logError("no such method '" + options + "' for slitslider self");
                    return;

                }

                self[options].apply(self, args);

            });

        }
        else {

            this.each(function () {

                if (self) {

                    self._init();

                }
                else {

                    self = $.data(this, 'slitslider', new $.Slitslider(options, this));

                }

            });

        }

        return self;

    };

})(jQuery, window);
